package org.example.utils;


import java.util.Random;

/**
 * 雪花算法
 */
public class SnowFlake {
    //开始时间戳
    // 初始时间戳(纪年)，可用雪花算法服务上线时间戳的值
    // 2024-07-17 15:21:35
    private final long twepoch = 1721200895L;

    //机器Id所占的位数
    private final long workerIdBits = 5L;

    //数据标识Id所占的位数
    private final long dataCenterIdBits = 5L;

    //支持的最大机器ID，结果是31（此移位算法可以很快地计算除几位二进制数所能表示的最大十进制数）
    private final long maxWorkerId = ~(-1L << workerIdBits);

    //支持的最大数据标识Id，结果是31
    private final long maxdataCenterId = ~(-1L << dataCenterIdBits);

    //序列在ID中所占的位数
    private final long sequenceBits = 12L;

    //机器Id向左移12位
    private final long workerIdShift = sequenceBits;

    //数据标识IdId向左移17位（12+5）
    private final long dataCenterIdShift = sequenceBits + workerIdBits;

    //时间戳向左移22位（5 + 5 + 12）
    private final long timestampleLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    //生成序列的掩码， 这里位4095（0b111111111111 = 0xfff = 4095）
    private final long sequenceMask = ~(-1L << sequenceBits);

    //工作机器Id（0～31）
    private volatile long workerId;

    //数据中心ID（0～31）
    private volatile long dataCenterId;

    //毫秒内序列（0～4095）
    private volatile long sequence = 0L;

    //上次生成ID的时间戳
    private volatile long lastTimestamap = -1L;
    // 随机数生成器
    private static final Random random = new Random();

    /**
     * 无参数构造函数，使用随机值
     */
    public SnowFlake() {
        this(getRandomWorkerId(), getRandomDataCenterId());
    }


    /**
     * 构造函数
     *
     * @param workerId     工作Id（0～31）
     * @param dataCenterId 数据中心ID（0～31）
     */


    public SnowFlake(long workerId, long dataCenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (dataCenterId > maxdataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxdataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
        this.sequence = 0L;
    }

    /**
     * 获取下一个Id（该方法是线程安全的）
     * 如果一个线程反复获取synchronized锁，那么synchronized锁将变成偏向锁
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        //如果当前时间小于上一次ID生成的时间戳，则说明系统时钟回退过，这个时候应该抛异常
        if (timestamp < lastTimestamap) {
            throw new RuntimeException(
                    String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamap - timestamp)
            );
        }
        //如果是同一时刻生成的，则进行毫秒内序列
        if (lastTimestamap == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒内序列溢出
            if (sequence == 0) {
                //阻塞到下一个毫秒，获得新的时间戳
                timestamp = tilNextMillis(lastTimestamap);
            }

        }

        //时间戳改变，毫秒内序列重置
        else {
            sequence = 0L;
        }

        //上次生成Id的时间戳
        lastTimestamap = timestamp;

        //移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - twepoch) << timestampleLeftShift) | (dataCenterId << dataCenterIdShift) | (workerId << workerIdShift) | sequence;


    }


    /**
     * 阻塞到下一个毫秒，直到获取新的时间戳
     *
     * @param lastTimestamap
     * @return
     */
    private long tilNextMillis(long lastTimestamap) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamap) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 生成随机工作ID
     *
     * @return 随机工作ID
     */
    private static long getRandomWorkerId() {
        return random.nextInt(32); // 0-31 之间的随机数
    }

    /**
     * 生成随机数据中心ID
     *
     * @return 随机数据中心ID
     */
    private static long getRandomDataCenterId() {
        return random.nextInt(32); // 0-31 之间的随机数
    }

}
 
 